#include "api/inc/fmod.hpp"
#include "api/inc/fmod_errors.h"
#include "wincompat.h"
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <limits.h>
#include <cerrno>
#include <iostream>
#include <assert.h>

using std::endl;
using std::cerr;
using std::cout;

#include "SnackMidi.h"
#include "CuePoints.h"
#include "Track.h"
#include "PlayManager.h"

#include "FMODSystem.h"
//#include "TrackImplementation.h"

//using namespace SnackBreak;

/*
// nice loop in Aaliyah - Are You That Somebody
SnackBreak::CuePoint aaliyahStart(0, 0.73576);
SnackBreak::CuePoint aaliyahEnd(16, 7.68400);

SnackBreak::CuePair aaliyah(aaliyahStart, aaliyahEnd);

// those about to groove loop sample
SnackBreak::CuePoint grooveStart(0, 0);
SnackBreak::CuePoint grooveEnd(8, 4.198095);

SnackBreak::CuePair groover(grooveStart, grooveEnd);
*/


/*
SnackBreak::CuePoint twilightStart(0, 1253.0 / 44100.0);
SnackBreak::CuePoint twilightEnd(16, 332017.0 / 44100.0);

SnackBreak::CuePair twilight(twilightStart, twilightEnd);
*/
#ifdef SNACK_ON_MAC
#define SoulOfMan_Sukdat				"/Volumes/Stuf/¬Other/Home Stuff/Very Good Music/Tracks/Soul of Man/Soul of Man - Sukdat.mp3"
#define AdamKSoha_Twilight				"/Volumes/Stuf/¬Other/Home Stuff/Very Good Music/Tracks/Adam K & Soha/Adam K & Soha - Twilight (original mix).mp3"
#define AlterEgo_WhyNot					"/Volumes/Stuf/¬Other/Home Stuff/Very Good Music/Tracks/Alter Ego/Alter Ego - Why Not!.mp3"
#define ArmandVanHelden_MyMyMy_Klass	"/Volumes/Stuf/¬Other/Home Stuff/Very Good Music/Tracks/Armand Van Helden/Armand Van Helden - My My My (Klaas mix).mp3"

#define BagRaiders_FunPunch_Whitenoise	"/Volumes/Stuf/¬Other/Home Stuff/Very Good Music/Tracks/Bag Raiders/Bag Raiders - Fun Punch (Whitenoise remix) .mp3"

#else // assume windows for now
#define SoulOfMan_Sukdat				"X:\\Music\\Tracks\\Soul of Man\\Soul of Man - Sukdat.mp3"
#define AdamKSoha_Twilight				"X:\\Music\\Tracks\\Adam K & Soha\\Adam K & Soha - Twilight (original mix).mp3"
#define AlterEgo_WhyNot					"X:\\Music\\Tracks\\Alter Ego\\Alter Ego - Why Not!.mp3"
#define ArmandVanHelden_MyMyMy_Klass	"X:\\Music\\Tracks\\Armand Van Helden\\Armand Van Helden - My My My (Klaas mix).mp3"
#define BagRaiders_FunPunch_Whitenoise	"X:\\Music\\Tracks\\Bag Raiders\\Bag Raiders - Fun Punch (Whitenoise remix) .mp3"

#define ThoseAboutToGrooveSample	"X:\\Archive\\samples\\Ray Parker Jr - For Those Who Like to Groove\\Breakdown loop 2.wav"
#endif
/*
SnackBreak::CuePoint sukdatStart(0, 47448.0 / 44100.0);
SnackBreak::CuePoint sukdatEnd(16, 373181.0 / 44100.0);

SnackBreak::CuePair sukdat(sukdatStart, sukdatEnd);
*/

int main(int argc, char *argv[])
{
	int               key = 0;

//	groover = SnackBreak::PlayManager::Get().CreateTrack(ThoseAboutToGrooveSample, SnackBreak::CuePair(0.0, 47448.0 / 44100.0, 16.0, 373181.0 / 44100.0));

	SnackBreak::Track* sukdat = SnackBreak::PlayManager::Get().CreateTrack(
		SoulOfMan_Sukdat, 
//		SnackBreak::CuePair(0.0, (47448.0 / 44100.0) - 0.03, 128.0, (2654282.0 / 44100.0) - 0.03), // experimenting with nudge (i.e. globally relevant beat point)
//		SnackBreak::CuePair(0.0, (47448.0 / 44100.0) + 0.02, 128.0, (2654282.0 / 44100.0) + 0.02), // good for the mix ith funpunch at 192
//		SnackBreak::CuePair(0.0, (47448.0 / 44100.0) + 0.04, 128.0, (2654282.0 / 44100.0) + 0.04), // good for the mix ith funpunch at 384
//		SnackBreak::CuePair(0.0, (47448.0 / 44100.0) + 0.07, 128.0, (2654282.0 / 44100.0) + 0.07), // good for the mix ith funpunch at 512
		SnackBreak::CuePair(0.0, (47448.0 / 44100.0) + 0.14, 128.0, (2654282.0 / 44100.0) + 0.14), // good for the mix ith funpunch at 768
		824.0
	); // enhanced accuracy sukdat
	SnackBreak::Track* twilight = SnackBreak::PlayManager::Get().CreateTrack(
		AdamKSoha_Twilight, 
		SnackBreak::CuePair(0.0, 1253.0 / 44100.0, 16.0, 332017.0 / 44100.0), 
		832.0
	);
	SnackBreak::Track* twilight2 = SnackBreak::PlayManager::Get().CreateTrack(
		AdamKSoha_Twilight, 
		SnackBreak::CuePair(0.0, 1253.0 / 44100.0, 16.0, 332017.0 / 44100.0), 
		832.0
	);
	SnackBreak::Track* funpunch = SnackBreak::PlayManager::Get().CreateTrack(
		BagRaiders_FunPunch_Whitenoise, 
		SnackBreak::CuePair(0.0, 1051.0 / 44100.0, 512.0, 10751510.0 / 44100.0), 
		662.0
	); // enhanced accuracy funpunch
/*
	SnackBreak::Track* whynot = SnackBreak::PlayManager::Get().CreateTrack(
		AlterEgo_WhyNot, 
		SnackBreak::CuePair(0.0, 2992.0 / 44100.0, 16.0, 339005.0 / 44100.0), 
		708.0
	);
	SnackBreak::Track* mymymyklass = SnackBreak::PlayManager::Get().CreateTrack(
		ArmandVanHelden_MyMyMy_Klass, 
		SnackBreak::CuePair(0.0, 7694.0 / 44100.0, 256.0, 5362160.0 / 44100.0), 
		704.0
	); // enhanced accuracy 
*/
	SnackBreak::Track* a;
	SnackBreak::Track* b;
//	a = sukdat;
	a = twilight;
	b = twilight2;
//	b = funpunch;



//	SnackBreak::PlayManager::Get().CreateEarlyMix(a, b, 4);
//	SnackBreak::PlayManager::Get().CreateBlend(a, b, 10, 64); // what's up with 64 64, have I a stupid api (or a flexible one)?
	double earlyStart = 64.0;
//	double earlyStart = 8.0;
	SnackBreak::PlayManager::Get().CreateEarlyBlend(a, b, earlyStart, 32);
	a->setPosition(earlyStart - 4);
	a->Play();
	
/*	SnackBreak::PlayManager::Get().CreateMix(sukdat, mymymyklass, sukdat->getLastBeatNum() - 4);
	SnackBreak::PlayManager::Get().CreateMix(mymymyklass, whynot, mymymyklass->getLastBeatNum() - 128);
	SnackBreak::PlayManager::Get().CreateMix(whynot, twilight, whynot->getLastBeatNum() - 128);*/
/*	SnackBreak::PlayManager::Get().CreateMix(sukdat, mymymyklass, 64);
	SnackBreak::PlayManager::Get().CreateMix(mymymyklass, whynot, 64);
	SnackBreak::PlayManager::Get().CreateMix(whynot, twilight, 64);*/

//	SnackBreak::PlayManager::Get().CreateMix(groover, sukdat);
//	SnackBreak::PlayManager::Get().CreateMix(twilight, sukdat, 256);
//	SnackBreak::PlayManager::Get().CreateMix(sukdat, twilight, 824 - 32);
//	SnackBreak::PlayManager::Get().CreateMix(sukdat, sukdat2, 824 - 4);
//	SnackBreak::PlayManager::Get().CreateMix(twilight, sukdat, 832 - 4);

	SnackMidi::EnumerateMidiInDevices();

	SnackMidi midiInput;
	long midiDev = 1;
	errno = 0;
	if ((argc > 2) && 
		(true || (midiDev=strtol(argv[2], NULL, 0))) && 
		(errno == 0))
	{
		midiInput.OpenInput(midiDev);
		cout << "Using device " << midiDev << endl;
	}

	// Main loop.
	do
	{
		if (kbhit())
		{
			key = getch();

			switch (key)
			{
				// space to play/pause
				case ' ' :
				{
					//bool paused;
					//channel->getPaused(&paused);
					//channel->setPaused(!paused);
					//sukdat->Pause();
					//twilight->Play();
					//twilight->SetPlayRate(0.73);
					break;
				}
			}
		}

		SnackBreak::Impl::System::Get().update(); // this is the chink in the abstraction armour - the event loop needs to move into playmanager, or the FMODSystem does (I think I prefer B)

/*		
		if (midiInput.CheckForNoteOn() > 0)
		{
			bool paused;
			channel->getPaused(&paused);
			channel->setPaused(!paused);
		}

		if (channel)
		{
			unsigned int ms;
			unsigned int lenms;
			bool         playing;
			bool         paused;

			channel->isPlaying(&playing);
			if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
			{
				Impl::ERRCHECK(result);
			}

			result = channel->getPaused(&paused);
			if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
			{
				Impl::ERRCHECK(result);
			}

			result = channel->getPosition(&ms, FMOD_TIMEUNIT_MS);
			if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
			{
				Impl::ERRCHECK(result);
			}

			result = sound->getLength(&lenms, FMOD_TIMEUNIT_MS);
			if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE))
			{
				Impl::ERRCHECK(result);
			}

			printf("\rTime %02d:%02d:%02d/%02d:%02d:%02d : %s", ms / 1000 / 60, ms / 1000 % 60, ms / 10 % 100, lenms / 1000 / 60, lenms / 1000 % 60, lenms / 10 % 100, paused ? "Paused " : playing ? "Playing" : "Stopped");
			fflush(stdout);
		}
*/
		Sleep(5);

	} while (key != 27);

	printf("\n");

	midiInput.CloseInput();
	
	return 0;
}
